package parse

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gitee.com/samhula/gospider/config"
	"gitee.com/samhula/gospider/task"

	"github.com/garyburd/redigo/redis"
)

// HTTP 请求代理
type HTTP struct {
	Congfig     config.Config
	DBRedisPool *DBRedisPool
	TaskPool    *task.TPool
}

// NewHTTP 产生一个HTTP对象
func NewHTTP(cfg config.Config) *HTTP {
	http := &HTTP{
		Congfig:     cfg,
		DBRedisPool: NewDBRedisPool(cfg.RedisConfig.Host),
		TaskPool:    task.NewTPool(5),
	}
	return http
}

// Start 获取总数
func (h *HTTP) Start(pageURL string, IP string, userAgent string) {
	h.TaskPool.Start()
	h.TaskPool.Heatbeat()
	h.TaskPool.AddTask(h.getTotalTask, pageURL, IP, userAgent)
	h.TaskPool.GetResult(func(r task.TResult) {
		kind := reflect.TypeOf(r.RES).Kind()
		switch kind {
		case reflect.Int:
			{
				total := r.RES.(int)
				if total == -1 {
					h.TaskPool.AddTask(h.getTotalTask, pageURL, h.ReturnIP(), h.GetAgent())
				} else {
					i := 1
					for i <= total {
						url := fmt.Sprintf("%v%d", h.Congfig.IPConfig.Host, i)
						i++
						h.TaskPool.AddTask(h.getPageTask, url, h.ReturnIP(), h.GetAgent())
					}
				}
			}
		case reflect.Ptr:
			{
				page := r.RES.(*Page)
				go h.SaveToRedis(page)
			}
		case reflect.String:
			{
				url := r.RES.(string)
				h.TaskPool.AddTask(h.getPageTask, url, h.ReturnIP(), h.GetAgent())
			}
		default:
			{
				fmt.Println("result:", reflect.TypeOf(r.RES).Kind(), kind, reflect.ValueOf(r))
			}
		}
	})
}

// Stop 停止
func (h *HTTP) Stop() {
	h.TaskPool.Stop()
}

func (h *HTTP) getTotalTask(args ...interface{}) interface{} {
	url := args[0].(string)
	ip := args[1].(string)
	agent := args[2].(string)
	total, err := h.GetTotals(url, ip, agent)
	if err != nil {
		fmt.Println("task:", err.Error(), total)
		return -1
	}
	return total
}

func (h *HTTP) getPageTask(args ...interface{}) interface{} {
	url := args[0].(string)
	ip := args[1].(string)
	agent := args[2].(string)
	page, err := h.GetPageIP(url, ip, agent)
	if err != nil {
		fmt.Println("url error:", err.Error())
		return fmt.Sprintf("%v", url)
	}
	return page
}

// GetTotals 获取总页数
func (h *HTTP) GetTotals(pageURL string, IP string, userAgent string) (int, error) {
	resp, err := h.GetResp(pageURL, IP, userAgent)
	if err != nil {
		return -1, err
	}
	parse, err := NewParse(resp)
	if err != nil {
		fmt.Println(err.Error())
		return -1, err
	}
	totals := parse.GetRuleText(parse.Document.Selection, h.Congfig.IPConfig.Totals, parse.Document.Url)
	if totals == "" {
		return -1, fmt.Errorf("获取规则不正确:%v", h.Congfig.IPConfig.Totals)
	}
	total, err := strconv.Atoi(totals)
	if err != nil {
		return -1, err
	}
	return total, nil
}

//SaveToRedis 把Page存储到Redis
func (h *HTTP) SaveToRedis(page *Page) {
	for _, item := range page.Items {
		ipInfo := make(map[string][]string)
		ipInfo[item.IP] = append(ipInfo[item.IP], item.IP, item.Port, item.Address, item.Anonymous, item.Type, item.Speed, item.Time)
		hBody, _ := json.Marshal(ipInfo[item.IP])
		//键值对的方式存入hash
		_, err := h.DBRedisPool.Do("SET", DBEnableIP, item.IP, string(hBody))
		if err != nil {
			fmt.Println("redis-error:", err.Error(), item.IP)
			os.Exit(1)
		}
	}
}

// GetPageIP 获取每一页内容
func (h *HTTP) GetPageIP(pageURL string, IP string, userAgent string) (*Page, error) {
	resp, err := h.GetResp(pageURL, IP, userAgent)
	if err != nil {
		// fmt.Println(err.Error())
		return nil, err
	}
	parse, err := NewParse(resp)
	if err != nil {
		// fmt.Println(err.Error())
		return nil, err
	}
	return parse.GetPage(&h.Congfig.IPConfig)
}

// GetResp 获取HTTP响应
func (h *HTTP) GetResp(URL string, IP string, userAgent string) (*http.Response, error) {
	request, err := http.NewRequest("GET", URL, nil)
	if err != nil {
		return nil, err
	}
	//随机返回User-Agent 信息
	request.Header.Set("User-Agent", userAgent)
	request.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
	request.Header.Set("Connection", "keep-alive")
	proxy, err := url.Parse(IP)
	//设置超时时间
	timeout := time.Duration(20 * time.Second)
	client := &http.Client{}
	if IP != "local" {
		client = &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyURL(proxy),
			},
			Timeout: timeout,
		}
	}
	response, err := client.Do(request)
	if err != nil {
		if IP != "local" && checkRequestError(err) {
			h.DBRedisPool.Del(proxy.Hostname(), DBExpireIP)
			h.DBRedisPool.Do("move", DBEnableIP, proxy.Hostname(), DBExpireIP)
			h.DBRedisPool.Do("move", DBValidateIP, proxy.Hostname(), DBExpireIP)
		}
		return nil, fmt.Errorf("line-99:遇到了错误-并切换ip %s %s", err.Error(), proxy)
	}
	if response.StatusCode != 200 {
		return nil, fmt.Errorf("StatusCode != %d:遇到了错误-并切换ip", response.StatusCode)
	}

	// 有效IP
	if IP != "local" {
		h.DBRedisPool.Del(proxy.Hostname(), DBValidateIP)
		h.DBRedisPool.Do("move", DBEnableIP, proxy.Hostname(), DBValidateIP)
	}
	return response, nil
}

// GetAgent 获取UserAgent
func (h *HTTP) GetAgent() string {
	agent := [...]string{
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
		"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0",
		"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
		"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
		"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
		"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
		"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
		"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
		"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
		"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	len := len(agent)
	return agent[r.Intn(len)]
}

// ReturnIP 随机返回IP
func (h *HTTP) ReturnIP() string {
	res := h.randomIP(DBValidateIP)
	if res == "" {
		res = h.randomIP(DBEnableIP)
	}
	//都找不到可用IP
	if res == "" {
		return "local"
	}
	res = strings.TrimLeft(res, "[")
	res = strings.TrimRight(res, "]")

	array := strings.Split(res, ",")

	for i := 0; i < len(array); i++ {
		array[i] = strings.Trim(array[i], "\"")
	}
	host := strings.ToLower(array[4]) + "://" + array[0] + ":" + array[1]

	return host
}

func (h *HTTP) randomIP(db int) string {
	key, err := redis.String(h.DBRedisPool.Do("randomkey", db))
	if err != nil || len(key) == 0 {
		return ""
	}
	res, err := redis.String(h.DBRedisPool.Do("GET", db, key))
	if err != nil || len(res) == 0 {
		return ""
	}
	return res
}

// 请求失败得把IP移到其他库
const (
	BlockRequest   string = "connection refused"
	LoopUPNoHost   string = "no such host"
	RequestTimeout string = "Client.Timeout exceeded while awaiting headers"
)

func checkRequestError(err error) bool {
	errStr := err.Error()
	if strings.Index(errStr, BlockRequest) != -1 {
		return true
	}
	if strings.Index(errStr, LoopUPNoHost) != -1 {
		return true
	}
	if strings.Index(errStr, RequestTimeout) != -1 {
		return true
	}
	return false
}
